home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / misc / volume25 / shql / part01 next >
Encoding:
Text File  |  1991-11-04  |  23.1 KB  |  949 lines

  1. Newsgroups: comp.sources.misc,comp.databases
  2. From: root%candle.uucp@ls.com (Bruce Momjian)
  3. Subject: v25i015:  shql - Interactively read and execute SQL commands, Part01/01
  4. Message-ID: <1991Nov5.030718.3073@sparky.imd.sterling.com>
  5. X-Md4-Signature: dad4edfe43522bb189c5b55cd7a7015c
  6. Date: Tue, 5 Nov 1991 03:07:18 GMT
  7. Approved: kent@sparky.imd.sterling.com
  8.  
  9. Submitted-by: root%candle.uucp@ls.com (Bruce Momjian)
  10. Posting-number: Volume 25, Issue 15
  11. Archive-name: shql/part01
  12. Environment: SQL, UNIX
  13.  
  14. SHQL is an interactive SQL database engine.  Written as a shell script,
  15. SHQL interprets SQL commands and manipulates flat files based on those
  16. commands.  SHQL is limited in its understanding of SQL constructs.  All
  17. this is outlined in the README file contained in the distribution.  A
  18. demo file is also included to show some examples.
  19.  
  20. The program has been tested by a group of 25 from comp.databases, and
  21. after several revisions, is ready to go.
  22.  
  23. Bruce Momjian           | 830 Blythe Avenue               | home: (215)853-3000
  24. root%candle.uucp@ls.com | Drexel Hill, Pennsylvania 19026 | work: (215)353-9879
  25. -------------------------------------------------------------------------
  26. #! /bin/sh
  27. # This is a shell archive, meaning:
  28. # 1. Remove everything above the #! /bin/sh line.
  29. # 2. Save the resulting text in a file.
  30. # 3. Execute the file with /bin/sh (not csh) to create:
  31. #    README
  32. #    demo.shql
  33. #    shql
  34. # This archive created: Thu Oct 31 21:06:06 1991
  35. export PATH; PATH=/bin:/usr/bin:$PATH
  36. echo shar: "extracting 'README'" '(2763 characters)'
  37. if test -f 'README'
  38. then
  39.     echo shar: "will not over-write existing file 'README'"
  40. else
  41. cat << \SHAR_EOF > 'README'
  42.                                   S H Q L
  43.  
  44.     Shql is a program that reads SQL commands interactively and
  45.     executes those commands by creating and manipulating Unix files.
  46.  
  47.     This program requires a bourne shell that understands functions,
  48.     as well as awk, grep, cut, sort, uniq, join, wc, and sed.
  49.  
  50.      This script can be invoked with the command 
  51.  
  52.         shql {database name}
  53.  
  54.       A directory must be created for the database before you may use it.
  55.     This directory will house all data files for a single database.
  56.     All datafiles are created with mode 666 ('rw-rw-rw-'), so create the
  57.     directory with 777 ('rwxrwxrwx') if you want the database to be 
  58.     sharable, and 700 ('rwx------') to be private.  Of course, multiple
  59.     databases are possible.  A database called 'mydb' may be created
  60.     as a directory $HOME/shql/mydb, or as $SHQL_ROOT/mydb, where
  61.     $SHQL_ROOT is defined below.
  62.  
  63.       The program is patterned after Ingres' interactive sql terminal
  64.     monitor program.  Terminal monitor commands begin with a back-slash
  65.     and must be alone on a line.  The \g is the 'go' command, \p is print,
  66.     and \q is quit.  Try 'help commands' for a full list.
  67.     
  68.     To get started, invoke shql with a database name.  Use the directory 
  69.     name you created above. Type
  70.  
  71.         shql mydb
  72.  
  73.     if the directory you created was 'mydb'.  Once shql starts up, you 
  74.     should see the database name displayed, and then a '*'. At this
  75.     point, the most valuable thing is to type help,
  76.  
  77.         * help
  78.         * \g
  79.  
  80.     You may then go on.  Try the demo.
  81.  
  82.     Shql can execute only one operation at a time, but operations can
  83.     be spread over several lines.
  84.  
  85.     Shql operations are limited to one table at a time so you can not
  86.     join tables with the usual tablename.fieldname convention,    
  87.     but you can create a view of two tables, and use that in a select.
  88.  
  89.     Subselects are implemented, but must be the last operand of a
  90.     'where' clause, most useful with 'in'.  A limited multi-table
  91.     join can be implemented with 'in' and subselect.
  92.  
  93.     Use white space liberally, especially around comparison operators,
  94.     equal signs, and parentheses.  In most cases, commas are optional.
  95.  
  96.     This SQL is type-less, so specify just the column width when creating
  97.     tables.  This is used only for display purposes.  Shql is
  98.     case-sensitive, and expects SQL key words to be in lower case.
  99.     
  100.     Aggregates (max, min, sum, count, etc.) and NULLs are not
  101.     implemented.
  102.  
  103.     Commands can be piped into shql, and the table data files are
  104.     tab delimited, so awk scripts can be used to generate reports 
  105.     directly from the tables.  Grave accents (`) may be used to 
  106.     execute unix command from with shql.  See the demo for an
  107.     example, i.e. "cat demo.shql | shql mydb".
  108.  
  109.     If you have comments, suggestions, or bug reports contact:
  110.  
  111.         Bruce Momjian (shql%candle.uucp@ls.com)
  112. SHAR_EOF
  113. fi
  114. echo shar: "extracting 'demo.shql'" '(1991 characters)'
  115. if test -f 'demo.shql'
  116. then
  117.     echo shar: "will not over-write existing file 'demo.shql'"
  118. else
  119. cat << \SHAR_EOF > 'demo.shql'
  120. # Demo for SHQL, version 0.60
  121. # Create table customer
  122. create table customer (
  123.     name 30,
  124.     age  3,
  125.     status 1 )
  126. \p\g
  127.  
  128. # Put one person in the table
  129. insert into customer values ( 'Fred', 32, 'G' )
  130. \p\g
  131.  
  132. # Study the table
  133. help customer
  134. \p\g
  135. select * from customer
  136. \p\g
  137.  
  138. # Add two more people
  139. insert into customer values 
  140. ( 'Barney', 29, 'G', 'Wilma', 28, 'D' )
  141. \p\g
  142. print customer
  143. \p\g
  144.  
  145. # Get customers with 'G' status
  146. select * from customer
  147. where status = 'G'
  148. \p\g
  149.  
  150. # Get sorted list of customers by age
  151. select * from customer
  152. order by age num
  153. \p\g 
  154.  
  155. # Make a table to hold customer status codes and their descriptions
  156. create table codes ( 
  157.     code 1,
  158.     description 10 )
  159. \p\g
  160.  
  161. # Insert status codes
  162. insert into codes values 
  163. ( 'G', 'Good', 'B', 'Bad', 'D', 'Dead Beat' )
  164. \p\g
  165.  
  166. # Create a view so we can see the customer name and status description
  167. create view custstat ( customer.status = codes.code )
  168. \p\g
  169.  
  170. # Look at the table
  171. help custstat
  172. \p\g
  173. select * from custstat
  174. \p\g
  175.  
  176. # Replace 'Barney' with 'Bad Bart'
  177. update customer 
  178. set name = 'Bad Bart', status = 'X' 
  179. where age = 29
  180. \p\g
  181.  
  182. print customer
  183. \p\g
  184.  
  185. # Get all customers that have invalid status'es
  186. select * from customer
  187. where status not in select code 
  188.             from codes
  189. \p\g
  190.  
  191. # Remove 'Fred'
  192. delete customer
  193. where age = 32
  194. \p\g
  195.  
  196. # Get rid of view 
  197. drop view custstat
  198. \p\g
  199.  
  200. # Create a holding table for old customers
  201. create table oldcust (
  202.     name 30,
  203.     status 1 )
  204. \p\g
  205.  
  206. # Copy old customer to new table
  207. insert into oldcust ( 
  208.     name status )
  209. select name status 
  210. from customer
  211. where age > 28
  212. \p\g
  213.  
  214. # Look at table
  215. print oldcust
  216. \p\g
  217.  
  218. # Delete customers moved over
  219. delete customer
  220. where age > 28
  221. \p\g
  222.  
  223. print customer
  224. \p\g
  225.  
  226. # Try a union of the two tables
  227. select name age
  228. from customer
  229. union
  230. select name status 
  231. from oldcust
  232. \p\g
  233.  
  234. # Show example of executing Unix commands
  235. insert into customer 
  236. values ( '`date`', `ls / | wc -l`, 'Y' )
  237. \p\g
  238. print customer
  239. \p\g
  240. # Clean up
  241. drop table codes
  242. \p\g
  243. drop table customer
  244. \p\g
  245. drop table oldcust
  246. \p\g
  247. \q    
  248. SHAR_EOF
  249. fi
  250. echo shar: "extracting 'shql'" '(16582 characters)'
  251. if test -f 'shql'
  252. then
  253.     echo shar: "will not over-write existing file 'shql'"
  254. else
  255. cat << \SHAR_EOF > 'shql'
  256. #! /bin/sh
  257. #
  258. # shql - version 1.0
  259. #
  260.  
  261. # DEFINE THESE
  262. SHQL_ROOT="/u/shql"        # system-wide database location
  263. EDITOR="${EDITOR:=/usr/bin/vi}" # default editor if EDITOR not defined
  264. SHELL="${SHELL:=/bin/sh}"     # default editor if EDITOR not defined
  265.  
  266. # Unix table file postfixes:  @ is attrib, ~ is data, % is view
  267.  
  268. #set -x           # uncomment for debugging
  269. #set -v
  270. UMASK=`umask`
  271. umask 0000        # share database
  272. trap "echo \"Goodbye\" ; rm -f /tmp/$$ /tmp/$$row" 0 1 2 3 15
  273. set -h            # remember functions
  274.  
  275. if echo '\c' | grep -s c ; then        # to adapt to System V vs. BSD 'echo'
  276.     NOCR1='-n'            # BSD
  277.     NOCR2=""
  278. else
  279.     NOCR1=""            # System V
  280.     NOCR2='\c'
  281. fi
  282. NL='
  283. '
  284. TAB='    '
  285. export _IFS TABLE CMD NOCR1 NOCR2 NL TAB
  286. _IFS="$IFS"
  287.  
  288. if [ "$1" = "" ]
  289. then    echo "Missing database name." 1>&2
  290.     echo "The database name must be a directory under $HOME/shql" 1>&2
  291.     echo "    or a directory under $SHQL_ROOT" 1>&2
  292.     exit 1
  293. fi
  294. echo "Database: $1"
  295.  
  296. if [ -r $HOME/shql/$1 ]
  297. then    cd $HOME/shql/$1
  298. elif [ -r $SHQL_ROOT/$1 ]
  299. then    cd $SHQL_ROOT/$1
  300. else     echo "Unknown database ($1)" 1>&2
  301.     echo "The database name must be a directory under $HOME/shql" 1>&2
  302.     echo "    or a directory under $SHQL_ROOT" 1>&2
  303.     exit 1
  304. fi
  305.  
  306.  
  307. #
  308. #**************************************************************************
  309. # syntax
  310. #**************************************************************************
  311. syntax(){
  312.     case "$1" in
  313.         create)    cat <<"END"
  314. CREATE TABLE table_name (
  315.     column_name column_width 
  316.     {, ...} 
  317. )     
  318. or
  319. CREATE VIEW view_name (
  320.     table_or_view1.column1 = table_or_view2.column2
  321. )
  322. END
  323. return 0
  324. ;;
  325.         delete) cat <<"END"
  326. DELETE 
  327. FROM table_name    
  328. { WHERE where_clause }
  329. END
  330. return 0
  331. ;;
  332.         drop) cat <<"END"
  333. DROP TABLE table_name
  334. or
  335. DROP VIEW view_name
  336. END
  337. return 0
  338. ;;
  339.         edit) cat <<"END"
  340. EDIT table_name
  341. is a non-standard method of changing a table's field names or display widths.
  342. END
  343. return 0
  344. ;;
  345.         help)    cat <<"END"
  346. HELP ALL
  347. or
  348. HELP TABLES 
  349. or
  350. HELP VIEWS
  351. or
  352. HELP COMMANDS
  353. or
  354. HELP [CREATE | DELETE | DROP | INSERT | SELECT | UPDATE | WHERE | PRINT | EDIT]
  355. or
  356. HELP table_name
  357. Commands must appear in lower case.
  358. END
  359. return 0
  360. ;;
  361.         insert) cat <<"END"
  362. INSERT INTO table_name 
  363.     { ( column_name, ... ) }
  364. VALUES ( expression, ...)
  365. or        
  366. INSERT INTO table_name 
  367.     { ( column_name, ... ) }
  368. subselect
  369. END
  370. return 0
  371. ;;
  372.         print) cat <<"END"
  373. PRINT table_name 
  374. is a non-standard synonym for SELECT * FROM table_name.
  375. END
  376. return 0
  377. ;;
  378.         select) cat <<"END"
  379. SELECT { DISTINCT } 
  380.     [ column_name {,...} | * ]
  381. FROM [ table_name | view_name ]
  382. { WHERE where_clause }
  383. { ORDER BY column_name { NUM } { ASC | DESC } {, ... }
  384. { UNION select statement }
  385. 'NUM' is a non-standard method for sorting numeric fields.
  386. END
  387. return 0
  388. ;;
  389.         update) cat <<"END"
  390. UPDATE table_name
  391. SET column_name = expression {, ... }
  392. { WHERE where_clause }
  393. END
  394. return 0
  395. ;;
  396.         where) cat <<"END"
  397. WHERE [ column_name | value ] [ =, !=, >, <, >=, <=, and, or, not, in ]
  398.       [ column_name | value | subselect ]
  399. Parentheses may be used to group expressions.  
  400. END
  401. return 0
  402. ;;
  403.     esac
  404.     return 1
  405. }
  406.  
  407. #
  408. #**************************************************************************
  409. # lookup_field
  410. #**************************************************************************
  411. lookup_field(){
  412.     if [ ! -f $TABLE% ]
  413.     then    RESULT="`grep -n \"^$1    \" $TABLE@`"
  414.     else     RESULT="`grep -n \"^$1    \" $TABLE@ | sed -n 1p`"
  415.     fi
  416.     if [ ! "$RESULT" ] 
  417.     then     OUTFIELD="$1"
  418.         return 1
  419.     else    OUTFIELDNUM="`expr "$RESULT" : '\([^:]*\)'`"
  420.         OUTFIELD="\$$OUTFIELDNUM" 
  421.         return 0
  422.     fi
  423. }
  424.  
  425. #
  426. #**************************************************************************
  427. # do_join 
  428. #**************************************************************************
  429. do_join(){
  430.     update_view "$1"
  431.     TABLE="$1"
  432.     lookup_field "$2" 
  433.     [ "$?" -ne 0 ] && echo "Bad view specifcation ($1.$2)" 1>&2 && return 1
  434.     JFIELD1="$OUTFIELDNUM"
  435.     JFIELD1L1="`expr $JFIELD1 - 1`"
  436.     update_view "$3"
  437.     TABLE="$3"
  438.     lookup_field "$4" 
  439.     [ "$?" -ne 0 ] && echo "Bad view specifcation ($3.$4)" 1>&2 && return 1
  440.     JFIELD2="$OUTFIELDNUM"
  441.     JFIELD2L1="`expr $JFIELD2 - 1`"
  442.  
  443.     ( grep "^$2    " $1@ ;
  444.       grep -v "^$2    " $1@ ;
  445.       grep -v "^$4    " $3@ ) > $5@
  446.     sort -t\     +$JFIELD2L1 $3~ > /tmp/$$
  447.     sort -t\     +$JFIELD1L1 $1~ | \
  448.         join -t\     -j1 $JFIELD1 -j2 $JFIELD2 \
  449.                         - /tmp/$$ > $5~
  450. }
  451.  
  452. #
  453. #**************************************************************************
  454. # update_view
  455. #**************************************************************************
  456. update_view(){
  457.     [ ! -f "$1%" ] && return 1
  458.     ( do_join `cat $1%` )
  459. }
  460.  
  461. #
  462. #**************************************************************************
  463. # where
  464. #**************************************************************************
  465. where(){
  466.     shift
  467.     while [ $# -gt 0 -a "$1" != "order" -a "$1" != "union" ]
  468.     do
  469.         [ "$1" = "select" ] &&
  470.             set - X `( SUBSELECT="Y" ;select "$@")` && shift
  471.         case "$1" in
  472.             and)     WHERE="$WHERE && ";;
  473.             or)    WHERE="$WHERE || ";;
  474.             not)    WHERE="$WHERE !" ;;
  475.             =)    WHERE="$WHERE == ";;
  476.             'in')     shift
  477.                 set - X `( SUBSELECT='Y';select "$@" )`
  478.                 shift
  479.                 INWHERE=""
  480.                 COMP="=="
  481.                 LOGIC="||"
  482.                 [ "$LAST" = "not" ] && COMP="=" && LOGIC="&&"
  483.                 for VALUE
  484.                 do
  485.                     [ "$INWHERE" != "" ] && 
  486.                         INWHERE="$INWHERE $LOGIC"
  487.                     INWHERE="$INWHERE ($WHERE$COMP $VALUE) "
  488.                 done
  489.                 WHERE="$INWHERE"
  490.                 break;;
  491.             *)    lookup_field "$1"
  492.                 WHERE="$WHERE $OUTFIELD";;
  493.         esac
  494.         LAST="$1"
  495.         shift
  496.     done 
  497.     [ "$WHERE" ] && WHERE=" ( $WHERE ) " && return 0
  498.     echo "Missing 'where' clause" 1>&2
  499.     syntax where
  500.     return 1
  501. }
  502.  
  503. #
  504. #**************************************************************************
  505. # help
  506. #**************************************************************************
  507. help(){
  508.     if [ ! "$2" ]
  509.     then    echo "Ambiguous syntax, try:" 1>&2 ; syntax help
  510.     elif [ "$2" = "all" ]
  511.     then    ls *@ *% | cut -d@ -f1 | cut -d% -f1 | uniq
  512.     elif [ "$2" = "tables" ] 
  513.     then    ls *@ *% | cut -d@ -f1 | cut -d% -f1 | uniq -u 
  514.     elif [ "$2" = "views" ] 
  515.     then    ls *% | cut -d% -f1 
  516.     elif [ "$2" = "commands" ]
  517.     then    cat << "END"
  518. \p is print
  519. \g is go(execute)
  520. \q is quit
  521. \e is edit
  522. \i is include
  523. \w is write
  524. \r is reset(clear)
  525. \s is shell
  526. \p\g print and go
  527. These commands must appear alone on a line.
  528. The number sign(#) may be used at the start of a line for comments.
  529. END
  530.     else    syntax $2 && return
  531.         TABLE="$2"
  532.         update_view "$TABLE"
  533.         if [ -f "$2@" ] 
  534.         then    echo "$NL <$2>" && cat "$2@"
  535.             [ -f "${2}%" ] &&echo $NOCR1 "$NL View:    $NOCR2" && 
  536.                 set - X `cat $2%` && shift &&
  537.                 echo "$1.$2 = $3.$4"
  538.             echo "$NL Rows:    "`cat $TABLE~ | wc -l`
  539.         else     echo "$TABLE does not exist." 1>&2
  540.             syntax help
  541.         fi
  542.     fi
  543. }
  544.  
  545. #
  546. #**************************************************************************
  547. # create
  548. #**************************************************************************
  549. create(){
  550.     shift
  551.     if [ -f "$2@" -o -f "$2%" ]
  552.     then    echo "Table already exists." 1>&2
  553.     elif [ "$1" = "view" -a $# -gt 2 ]
  554.     then    shift
  555.         if [ $# -ne 6 ]
  556.         then     syntax create
  557.         else     
  558.             [ "X$2" != "X(" ] && echo "Bad syntax" 1>&2 && 
  559.                             syntax create && return
  560.             TABLE1="`expr $3 : '\([^\.]*\)'`"
  561.             FIELD1="`expr $3 : '[^\.]*.\(.*\)'`"
  562.             TABLE="$TABLE1"
  563.             lookup_field "$FIELD1" 
  564.             [ "$?" -ne 0 ] && echo "Bad table or field name" 1>&2 &&
  565.                                     return
  566.             [ "X$4" != "X=" ] && echo "Bad syntax" 1>&2 && 
  567.                             syntax create && return
  568.             TABLE2="`expr $5 : '\([^\.]*\)'`"
  569.             FIELD2="`expr $5 : '[^\.]*.\(.*\)'`"
  570.             TABLE="$TABLE2"
  571.             lookup_field "$FIELD2" 
  572.             [ "$?" -ne 0 ] && echo "Bad table or field name" 1>&2 &&
  573.                                     return
  574.             [ "X$2" != "X(" ] && echo "Bad syntax" 1>&2 && 
  575.                             syntax create && return
  576.             echo "$TABLE1 $FIELD1 $TABLE2 $FIELD2 $1" > $1%
  577.             update_view "$1"            
  578.         fi
  579.         echo "OK"
  580.     elif [ "$1" = "table" -a $# -ge 5 ] 
  581.     then
  582.         [ "X$3" != "X(" ] && echo "Bad syntax" 1>&2 && 
  583.                             syntax create && return
  584.         TABLE="$2"
  585.         shift 3
  586.         > $TABLE@
  587.         > $TABLE~
  588.         while [ $# -ge 2 ]
  589.         do
  590.             echo "$1    $2" >> $TABLE@
  591.             shift 2
  592.         done
  593.         [ "X$1" != "X)" ] && echo "Bad syntax" 1>&2 && 
  594.                     rm -f $TABLE@ && syntax create && return
  595.         echo "OK"
  596.     else 
  597.         echo "Improper syntax ($1)" 1>&2
  598.         syntax create
  599.     fi
  600.     return
  601. }
  602.  
  603. #
  604. #*************************************************************************
  605. # drop
  606. #**************************************************************************
  607. drop(){
  608.     [ "$2" != "table" -a "$2" != "view" ] && 
  609.         echo "Syntax error." 1>&2 && syntax drop && return
  610.     [ "$2" = "table" -a -f "$3%" ] &&
  611.         echo "Can not drop, $2 is a view, not a table" 1>&2 && return
  612.     [ "$2" = "view" -a ! -f "$3%" ] &&
  613.         echo "Can not drop, $2 is not a view" 1>&2 && return
  614.     if [ -f "$3@" -o -f "$3%" ] 
  615.     then    rm -f $3@ $3~ $3%
  616.         echo "OK"
  617.     else      echo "No such table" 1>&2
  618.     fi
  619. }
  620.  
  621. #
  622. #**************************************************************************
  623. # insert
  624. #**************************************************************************
  625. insert(){
  626.     shift
  627.     [ "$1" != "into" ] && echo "Improper syntax ($1)" 1>&2 && 
  628.         syntax insert && return
  629.     shift
  630.     TABLE="$1"
  631.     update_view "$TABLE" && echo "Can not insert into a view" 1>&2 && return
  632.     [ ! -f "$TABLE@" ] && echo "Table does not exist" 1>&2 && return
  633.     shift
  634.     ATTRIB="`cat $TABLE@ | wc -l`"
  635.     XASGN=""
  636.     XECHO="echo \""
  637.     if [ $# -gt 0 -a "X$1" = "X(" ]
  638.     then    ATTRIB2="0"
  639.         shift
  640.         while [ $# -gt 0 -a "X$1" != "X)" ]
  641.         do
  642.             lookup_field "$1" 
  643.             [ "$?" -ne 0 ] && echo "Bad field name. ($1)" 1>&2 && 
  644.                                     return 
  645.             XASGN="$XASGN X$OUTFIELDNUM=\`eval echo \$1\` ; shift;"
  646.             shift
  647.             ATTRIB2=`expr $ATTRIB2 + 1`
  648.         done
  649.         [ "X$1" != "X)" ] && echo "Syntax error ($1)" 1>&2 && 
  650.                         syntax insert && return
  651.         shift
  652.         POS="1"
  653.         while [ "$POS" -le "$ATTRIB" ]
  654.         do
  655.             eval X$POS=""
  656.             [ "$POS" != "1" ] && XECHO="$XECHO\$TAB"
  657.             XECHO="$XECHO\$X$POS"
  658.             POS=`expr $POS + 1`
  659.         done
  660.         XECHO="$XECHO\""
  661.         ATTRIB="$ATTRIB2"
  662.     fi    
  663.     if [ "$1" = "select" ]
  664.     then     eval set - X "`( SUBSELECT='Y' ; select "$@" )` \)" ; shift
  665.     elif [ "$1" != "values" -o "X$2" != 'X(' ]  
  666.         then     echo "Improper syntax ($1)" 1>&2 && syntax insert && 
  667.                                     return
  668.     else    shift 2
  669.     fi
  670.     for LAST do 
  671.     : ; done
  672.     [ "X$LAST" != "X)" ] && 
  673.         echo "Improper syntax" 1>&2 && syntax insert && return
  674.     if [ "`expr \( $# - 1 \) % $ATTRIB`" -ne 0 ]
  675.     then     echo "Incorrect number of values." 1>&2
  676.     else    ROWS="`expr \( $# - 1 \) / $ATTRIB`"
  677.         while [ $# -gt 1 ]
  678.         do    
  679.             if [ "$XASGN" = "" ]
  680.             then     
  681.                 echo $NOCR1 "`eval echo $1`$NOCR2" >> $TABLE~ 
  682.                 shift
  683.                 while [ "`expr \( $# - 1 \) % $ATTRIB`" -ne 0 ]
  684.                 do
  685.                     echo $NOCR1 "$TAB`eval echo $1`$NOCR2"\
  686.                                  >> $TABLE~
  687.                     shift
  688.                 done
  689.                 echo "" >> $TABLE~
  690.             else    eval $XASGN
  691.                 eval $XECHO >> $TABLE~
  692.             fi
  693.         done
  694.         echo "($ROWS rows)"            
  695.     fi
  696. }
  697.  
  698. #
  699. #*************************************************************************
  700. # delete
  701. #**************************************************************************
  702. delete(){
  703.     TABLE="$2"
  704.     update_view "$TABLE" && echo "You can not delete from a view." 1>&2 &&
  705.                                     return
  706.     [ ! -f "$TABLE@" ] && echo "$TABLE does not exit." 1>&2 && return
  707.     WHERE=""
  708.     if [ "$3" = "where" ]
  709.     then     shift 2
  710.         where "$@" && 
  711.         awk -F"    " "! $WHERE { cnt += 1 ; print } 
  712.             END { printf \"( %1d rows.)\\n\", (NR - cnt) \
  713.             >\"/tmp/$$row\" }" $TABLE~ > /tmp/$$ && 
  714.             mv /tmp/$$ $TABLE~ && cat /tmp/$$row
  715.     else    echo '('`cat $TABLE~ | wc -l`' rows)' 
  716.         > $TABLE~
  717.     fi
  718. }
  719.  
  720. #
  721. #*************************************************************************
  722. # update
  723. #**************************************************************************
  724. update(){
  725.     TABLE="$2"
  726.     update_view "$TABLE" && echo "Can not update a view." 1>&2 && return
  727.     [ ! -f "$TABLE@" ] && echo "$TABLE does not exit." 1>&2 && return
  728.     [ "$3" != "set" ] && echo "Improper syntax." 1>&2 && syntax update && 
  729.                                     return
  730.     shift 3
  731.     ASSIGN=""
  732.     while [ $# -gt 0 -a "$1" != "where" ]
  733.     do
  734.         lookup_field "$1" && [ "$2" = "=" ] && ASSIGN="$ASSIGN ; "
  735.         ASSIGN="$ASSIGN $OUTFIELD"
  736.         shift
  737.     done
  738.     WHERE=""
  739.     if [ "$1" = "where" ] 
  740.     then     where "$@" || return 
  741.     fi
  742.     awk -F"    " "BEGIN { OFS = \"    \" }
  743.         $WHERE     { $ASSIGN; cnt += 1 }
  744.             { print } 
  745.         END     { printf \"( %1d rows)\\n\", cnt >\"/tmp/$$row\" }" \
  746.         $TABLE~ > /tmp/$$ && 
  747.             mv /tmp/$$ $TABLE~ && cat /tmp/$$row
  748. }
  749.  
  750. #
  751. #**************************************************************************
  752. # select
  753. #**************************************************************************
  754. select(){
  755.     UNION="Y"
  756.     while [ "$UNION" != "" ]
  757.     do
  758.         FROM=""
  759.         UNION=""
  760.         for TABLE 
  761.         do
  762.             [ "$FROM" ] && break
  763.             [ "$TABLE" = "from" ] && FROM="Y"
  764.         done
  765.         [ ! "$FROM" ] && echo "Syntax error." 1>&2 && syntax select &&
  766.                                     return
  767.         [ ! -f "$TABLE@" ] && echo "$TABLE does not exist." 1>&2 && 
  768.                                     return
  769.         update_view "$TABLE"
  770.          shift
  771.         DISTINCT=""
  772.         [ "$1" = "distinct" ] && DISTINCT="Y" && shift
  773.         FIELDS=""
  774.         PRINTF=""
  775.         while [ "$1" != "from" ]
  776.         do
  777.             if [ "$1" = '*' ]
  778.             then    shift
  779.                 set - X `cat $TABLE@ | cut -d\     -f1` "$@" 
  780.                 shift
  781.             else    lookup_field "$1" 
  782.                 [ "$?" -ne 0 ] && \
  783.                     echo "Bad field name ($1)" 1>&2 && 
  784.                                     return
  785.                 [ "$FIELDS" ] && FIELDS="$FIELDS,"
  786.                 FIELDS="$FIELDS $OUTFIELD"
  787.                 if [ "$SUBSELECT" = "" ]
  788.                 then     [ ! "$PRINTF" ] && PRINTF="|"
  789.                     WIDTH=`expr "$RESULT" : \
  790.                         '[^    ]*    \(.*\)'`
  791.                     PRINTF="$PRINTF%-$WIDTH.${WIDTH}s|"
  792.                 else    [ "$PRINTF" ] && 
  793.                     PRINTF="$PRINTF    "
  794.                     PRINTF="$PRINTF\\\"%s\\\""
  795.                 fi
  796.                 shift
  797.             fi
  798.         done
  799.         shift 2
  800.         WHERE=""
  801.         SORT=""
  802.         while [ $# -ne 0 ]
  803.         do    
  804.             if [ "$1" = "where" ]
  805.             then
  806.                 where "$@" && 
  807.                 WHERE="$WHERE || NR == 1"
  808.                 shift
  809.             elif [ "$1" = "order" ]
  810.             then     [ "$2" != "by" ] && 
  811.                     echo "Syntax error ($2)" 1>&2 && 
  812.                     syntax select && return
  813.                 shift 2
  814.                 while [ $# -gt 0 -a "$1" != "union" ]
  815.                 do
  816.                     if [     "$1" != "asc" -a \
  817.                         "$1" != "desc" -a \
  818.                         "$1" != "num" ] 
  819.                     then    lookup_field "$1" 
  820.                         [ "$?" -ne 0 ] &&
  821.                 echo "Bad field name ($1)" 1>&2 && return 
  822.                         [ "$SORT" = "" ] && 
  823.                             SORT="sort -t\"    \" "
  824.                         SORTL="`expr $OUTFIELDNUM - 1`"
  825.                         SORT="$SORT +$SORTL"
  826.                         [ "$2" = "num" ] && 
  827.                             SORT="${SORT}n"
  828.                         [ "$2" = "desc" ] && 
  829.                             SORT="${SORT}r"
  830.                         [ "$3" = "desc" ] && 
  831.                             SORT="${SORT}r"
  832.                         SORT="$SORT -$OUTFIELDNUM"
  833.                     fi
  834.                     shift
  835.                 done
  836.             elif [ "$1" = "union" ]
  837.             then    shift
  838.                 UNION="Y"
  839.                 break
  840.             else    shift
  841.             fi
  842.         done
  843.         
  844.         if [ "$DISTINCT" != "" ] 
  845.         then    if [ "$SORT" = "" ]
  846.             then    DIST="sort | uniq | tee /tmp/$$row"
  847.             else    DIST="uniq | tee /tmp/$$row"
  848.             fi
  849.         else    DIST="cat"
  850.         fi                    
  851.  
  852.         TABLEFILE="$TABLE~"
  853.         [ "$SORT" != "" ] && cat $TABLE~ | eval "$SORT" > /tmp/$$ &&
  854.                             TABLEFILE="/tmp/$$"
  855.  
  856.         if [ "$SUBSELECT" != "" ] 
  857.         then    awk -F"    " "$WHERE {printf \"$PRINTF \", $FIELDS }" \
  858.                             $TABLEFILE |eval "$DIST"
  859.         else     ( set - X `cut -d\     -f1 $TABLE@` ; shift 
  860.               echo $NOCR1 "-$1-$NOCR2" ; shift 
  861.                 for HEADING 
  862.               do 
  863.                 echo $NOCR1 "$TAB-$HEADING-$NOCR2" 
  864.                done ; echo "" ) |
  865.             awk -F"    " \
  866.             "$WHERE { cnt += 1 ; printf \"$PRINTF\\n\", $FIELDS }
  867.             END    { printf \"( %1d rows)\\n\", (cnt - 1) \
  868.             >\"/tmp/$$row\" }" - $TABLEFILE | eval "$DIST" \
  869.                 && if [ "$DISTINCT" = "" ]
  870.                 then    cat /tmp/$$row
  871.                 else     X=`expr \`cat /tmp/$$row|wc -l\` - 1`
  872.                     echo '('$X' rows)' 
  873.                 fi
  874.         fi
  875.     done
  876. }    
  877.  
  878. #
  879. #**************************************************************************
  880. # main
  881. #**************************************************************************
  882. while :
  883. do
  884.     while :
  885.     do
  886.         echo $NOCR1 "* $NOCR2"
  887.         read LINE || exit 
  888.         case "$LINE" in 
  889.             p)  echo "$_CMD";;
  890.             g)  break;;
  891.             pg) echo "$_CMD" ; break ;;
  892.             r)  echo "reset" ; _CMD="";;
  893.             s)  umask $UMASK ; $SHELL ; umask 0000;;
  894.             e)  umask $UMASK ; echo "$_CMD" > /tmp/$$
  895.                 $EDITOR /tmp/$$; _CMD="`cat /tmp/$$`"
  896.                 umask 0000;;
  897.             i)  echo $NOCR1 "Enter include file: $NOCR2" 
  898.                 read LINE  
  899.                 [ -f "$LINE" ] && _CMD="$_CMD`cat $LINE`$NL" &&
  900.                 echo "$LINE included";;
  901.             w)  echo $NOCR1 "Enter output file: $NOCR2" 
  902.                 read LINE  
  903.                 [ "$LINE" ] && umask $UMASK && 
  904.                 echo "$_CMD" > "$LINE" && umask 0000 && 
  905.                 echo "$LINE written";;
  906.             q)  exit 0;; 
  907.             \#*) [ "$NEW" = "Y" ] && _CMD="" ;;
  908.             *)  [ "$NEW" = "Y" ] && _CMD=""
  909.                 _CMD="$_CMD$LINE$NL";;
  910.         esac
  911.         NEW=""
  912.     done
  913.  
  914.     CMD=`echo "$_CMD" | sed -e "s/\'/\"/g" \
  915.         -e 's/\"\([^\"]*\)\"/\"\\\"\1\\\"\"/g' \
  916.         -e 's/</\\\</g' -e 's/>/\\\>/g' -e 's/\*/\\\*/g' \
  917.         -e 's/(/\\\(/g' -e 's/)/\\\)/g'`
  918.     [ ! "$CMD" ] && continue
  919.     IFS="$_IFS,"
  920.     eval set - X $CMD
  921.     shift
  922.     IFS="$_IFS"
  923.     NEW="Y"
  924.     case $1 in
  925.         select)     select "$@";; 
  926.         create)     create "$@";;
  927.         delete)     delete "$@";;
  928.         drop)         drop "$@";;
  929.         insert)     insert "$@";;
  930.         update)     update "$@";;
  931.         edit)        [ "$2" ] && $EDITOR $2@;;
  932.         help)        help "$@";;
  933.         print)        select "select" '*' "from" "$2";;
  934.         *)         echo "Missing or unrecognized command." 1>&2 ;;
  935.     esac
  936. done
  937.  
  938. SHAR_EOF
  939. chmod +x 'shql'
  940. fi
  941. exit 0
  942. #    End of shell archive
  943. exit 0 # Just in case...
  944. -- 
  945. Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
  946. Sterling Software, IMD           UUCP:     uunet!sparky!kent
  947. Phone:    (402) 291-8300         FAX:      (402) 291-4362
  948. Please send comp.sources.misc-related mail to kent@uunet.uu.net.
  949.